package com.ruoyi.framework.security.service;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUserPost;
import com.ruoyi.project.system.domain.SysUserRole;
import com.ruoyi.project.system.mapper.*;
import com.ruoyi.project.system.service.ISysDeptService;
import com.ruoyi.tools.pay.domain.WxPayBean;
import com.ruoyi.tools.redis.domain.RedisConfigProperties;
import com.ruoyi.tools.util.http.BackEndHttpRequest;
import com.ruoyi.tools.wechat.OpenIdTools;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.framework.security.RegisterBody;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.service.ISysConfigService;
import com.ruoyi.project.system.service.ISysUserService;

import javax.annotation.Resource;
import java.util.*;

import static com.ruoyi.tools.util.param.ParamsUtil.ParamBack;

/**
 * 注册校验方法
 * 
 * @author ruoyi
 */
@Component
public class SysRegisterService
{
    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private RedisCache redisCache;

    /**
     * 注册
     */
    public String register(RegisterBody registerBody)
    {
        String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword();
        SysUser sysUser = new SysUser();
        sysUser.setUserName(username);

        // 验证码开关
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        if (captchaEnabled)
        {
            validateCaptcha(username, registerBody.getCode(), registerBody.getUuid());
        }

        if (StringUtils.isEmpty(username))
        {
            msg = "用户名不能为空";
        }
        else if (StringUtils.isEmpty(password))
        {
            msg = "用户密码不能为空";
        }
        else if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            msg = "账户长度必须在2到20个字符之间";
        }
        else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            msg = "密码长度必须在5到20个字符之间";
        }
        else if (!userService.checkUserNameUnique(sysUser))
        {
            msg = "保存用户'" + username + "'失败，注册账号已存在";
        }
        else
        {
            sysUser.setPhonenumber(registerBody.getPhone());
            sysUser.setOpenId(registerBody.getOpenId());

            sysUser.setInviterId(registerBody.getInviterId());
            sysUser.setTreeLevel(registerBody.getTreeLevel());
            sysUser.setTopInviterId(registerBody.getTopInviterId());
            sysUser.setInviterTree(registerBody.getInviterTree());
            sysUser.setCity(registerBody.getCity());
            sysUser.setCityCode(registerBody.getCityCode());

            sysUser.setDeptId(registerBody.getDeptId());
            if(StringUtils.isBlank(registerBody.getNickName())){
                sysUser.setNickName(username);
            }else{
                sysUser.setNickName(registerBody.getNickName());
            }

            sysUser.setPassword(SecurityUtils.encryptPassword(password));

            sysUser.setAvatar(registerBody.getIcon());//头像

            boolean regFlag = userService.registerUser(sysUser);
            if (!regFlag)
            {
                msg = "注册失败,请联系系统管理人员";
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")));
            }
        }
        return msg;
    }

    /**
     * 校验验证码
     * 
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null)
        {
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha))
        {
            throw new CaptchaException();
        }
    }

    //--------------------------------微信--------------------------------

    @Autowired(required = false)
    private OpenIdTools tools;

    @Autowired(required = false)
    private RedisConfigProperties redisConfig;

    @Autowired(required = false)
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysLoginService loginService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private SysRegisterService registerService;

    @Autowired
    private SysUserRoleMapper userRoleMapper;

    @Autowired
    private SysUserPostMapper userPostMapper;

    @Autowired
    private ISysDeptService deptService;


    public AjaxResult wxLogin(RegisterBody user) {
        Long deptId = user.getDeptId();//部门id
        String phone = user.getPhone();//电话
        String code = user.getWxCode();//微信code
        Long inviterId = user.getInviterId();//邀请人id

        //响应结果
        AjaxResult ajax = AjaxResult.success();

        //获取openId
        Map openIdMap = tools.getOpenId(code);
        String openId = openIdMap.get("openid").toString();
        System.out.println("===========");
        System.out.println(openId);

        //校验手机号是否注册
        SysUser info = userMapper.checkPhoneUnique(phone);
        String lastSixCharacters = phone.substring(Math.max(0, phone.length() - 6));

        //已注册
        if (StringUtils.isNotNull(info))
        {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(info.getPhonenumber(), "systemUser"+lastSixCharacters);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            loginService.recordLoginInfo(loginUser.getUserId());
            // 生成token
            String token = tokenService.createToken(loginUser);
            ajax.put("code",200);
            ajax.put(Constants.TOKEN, token);
            return ajax;

        }else{//注册
            RegisterBody rUser = new RegisterBody();
            rUser.setCity(user.getCity());
            rUser.setCityCode(user.getCityCode());
            rUser.setUsername(phone);
            rUser.setPhone(phone);

            rUser.setPassword("systemUser"+lastSixCharacters);//【user+手机号码】作为密码

            rUser.setOpenId(openId);
            if(deptId == null){
                ajax.put("code",500);
                ajax.put("msg","上级部门校验失败");
                return ajax;
            }else{
                SysDept sysDept = deptService.selectDeptById(deptId);
                if(ObjectUtils.isEmpty(sysDept)){
                    ajax.put("code",500);
                    ajax.put("msg","部门不存在");
                    return ajax;
                }
            }
            rUser.setDeptId(deptId);
            if(inviterId!=null){
                //校验上级是否存在 补全层级信息
                SysUser sysUser = userService.selectUserById(inviterId);
                if(ObjectUtils.isEmpty(sysUser)){
                    ajax.put("code",500);
                    ajax.put("msg","上级不存在");
                    return ajax;
                }

                // 用于存储所有上级ID的列表
                List<Long> inviterIds = new ArrayList<>();

                // 当邀请人ID为0时，表示没有上级了
                while (inviterId != null && inviterId != 0) {
                    // 将邀请人ID添加到列表中
                    inviterIds.add(inviterId);

                    // 通过邀请人ID查找上级用户
                    SysUser sysUser1 = userService.selectUserById(inviterId);

                    // 获取邀请人的上级ID
                    inviterId = sysUser1.getInviterId();
                }

                // 将所有上级ID转换成字符串数组
                String[] inviterIdArray = inviterIds.stream().map(String::valueOf).toArray(String[]::new);

                // 计算当前用户所处的层级
                int userLevel = inviterIds.size();

                // 将拼接好的字符串数组赋值给用户性能对象
                String inviterIdString = String.join(",", inviterIdArray);
                rUser.setInviterTree(inviterIdString);

                // 将层级赋值给用户性能对象
                rUser.setTreeLevel((long) userLevel);//邀请树
                rUser.setTopInviterId(inviterIds.get(inviterIds.size() - 1));//最上级
                rUser.setInviterId(inviterIds.get(0));//邀请人id
            }else{
                rUser.setInviterId(0L);
                rUser.setTreeLevel(0L);
            }

            //注册信息
            String register = registerService.register(rUser);

            //设置默认角色
            SysUser suser = new SysUser();
            Long[] rols = {2L};
            suser.setRoleIds(rols);
            SysUser info2 = userMapper.checkPhoneUnique(phone);
            suser.setUserId(info2.getUserId());
//            insertUserPost(suser);//设置岗位
            insertUserRole(suser);//设置角色

            // 生成令牌
            String token = loginService.login(phone,"systemUser"+lastSixCharacters, null,
                    null);
            ajax.put(Constants.TOKEN, token);
            return ajax;
        }
    }

    /**
     * 新增用户岗位信息
     *
     * @param user 用户对象
     */
    public void insertUserPost(SysUser user)
    {
        Long[] posts = user.getPostIds();
        if (StringUtils.isNotEmpty(posts))
        {
            // 新增用户与岗位管理
            List<SysUserPost> list = new ArrayList<SysUserPost>(posts.length);
            for (Long postId : posts)
            {
                SysUserPost up = new SysUserPost();
                up.setUserId(user.getUserId());
                up.setPostId(postId);
                list.add(up);
            }
            userPostMapper.batchUserPost(list);
        }
    }

    /**
     * 新增用户角色信息
     *
     * @param user 用户对象
     */
    public void insertUserRole(SysUser user)
    {
        this.insertUserRole(user.getUserId(), user.getRoleIds());
    }

    /**
     * 新增用户角色信息
     *
     * @param userId 用户ID
     * @param roleIds 角色组
     */
    public void insertUserRole(Long userId, Long[] roleIds)
    {
        if (StringUtils.isNotEmpty(roleIds))
        {
            // 新增用户与角色管理
            List<SysUserRole> list = new ArrayList<SysUserRole>(roleIds.length);
            for (Long roleId : roleIds)
            {
                SysUserRole ur = new SysUserRole();
                ur.setUserId(userId);
                ur.setRoleId(roleId);
                list.add(ur);
            }
            userRoleMapper.batchUserRole(list);
        }
    }

    //--------------------------------公众号--------------------------------

    @Autowired
    private WxPayBean wxPayBean;

    public Map<String, Object> loginNoSilent(RegisterBody ruser) {

        Map<String, Object> map = new HashMap<>();

        //认证服务器
        String url1="https://api.weixin.qq.com/sns/oauth2/access_token";
        Map<String,Object> m =new LinkedHashMap<>();
        m.put("appid",wxPayBean.getAppId());
        m.put("secret",wxPayBean.getAppSecret());
        m.put("code",ruser.getCode());
        m.put("grant_type","authorization_code");
        String str= BackEndHttpRequest.sendPost(url1, ParamBack(m));
        Map maps = (Map) JSONObject.parseObject(str);

        //第三步：拉取用户信息
        String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?" +
                "access_token=" + maps.get("access_token") +
                "&openid=" + maps.get("openid") +
                "&lang=zh_CN";

        String userInfo = BackEndHttpRequest.sendGet(userInfoUrl,"");
        JSONObject userJson = JSONObject.parseObject(userInfo);

        String openId = (String) userJson.get("openid");
        String nickname = (String) userJson.get("nickname");
        String headimgurl = (String) userJson.get("headimgurl");

        System.out.println(userJson);

        if(org.apache.commons.lang3.StringUtils.isBlank(openId)){
            return AjaxResult.error("登录失败");
        }

        //密码后缀
        String lastSixCharacters = openId.substring(Math.max(0, openId.length() - 6));

        //通过openid查询账号是否存在
        SysUser sysuser = userMapper.queryUserByOpenid(openId);
        if(ObjectUtils.isEmpty(sysuser)){//注册
            RegisterBody rUser = new RegisterBody();
            rUser.setUsername(openId);
            rUser.setOpenId(openId);
            rUser.setPassword("systemUser"+lastSixCharacters);//【user+手机号码】作为密码

            rUser.setIcon(headimgurl);
            rUser.setNickName(nickname);

            //设置部门
            Long deptId = rUser.getDeptId();
            if(deptId == null){
                map.put("code",500);
                map.put("msg","部门不能为空");
                return map;
            }else{
                SysDept sysDept = deptService.selectDeptById(deptId);
                if(ObjectUtils.isEmpty(sysDept)){
                    map.put("code",500);
                    map.put("msg","部门不存在");
                    return map;
                }
            }
            rUser.setDeptId(deptId);

            Long inviterId = rUser.getInviterId();
            if(inviterId!=null){
                //TODO 校验上级是否存在 补全层级信息
                SysUser sysUser = userService.selectUserById(inviterId);
                if(ObjectUtils.isEmpty(sysUser)){
                    map.put("code",500);
                    map.put("msg","上级不存在");
                    return map;
                }

                // 用于存储所有上级ID的列表
                List<Long> inviterIds = new ArrayList<>();

                // 当邀请人ID为0时，表示没有上级了
                while (inviterId != null && inviterId != 0) {
                    // 将邀请人ID添加到列表中
                    inviterIds.add(inviterId);

                    // 通过邀请人ID查找上级用户
                    SysUser sysUser1 = userService.selectUserById(inviterId);

                    // 获取邀请人的上级ID
                    inviterId = sysUser1.getInviterId();
                }

                // 将所有上级ID转换成字符串数组
                String[] inviterIdArray = inviterIds.stream().map(String::valueOf).toArray(String[]::new);

                // 计算当前用户所处的层级
                int userLevel = inviterIds.size();

                // 将拼接好的字符串数组赋值给用户性能对象
                String inviterIdString = String.join(",", inviterIdArray);
                rUser.setInviterTree(inviterIdString);

                // 将层级赋值给用户性能对象
                rUser.setTreeLevel((long) userLevel);//邀请树
                rUser.setTopInviterId(inviterIds.get(userLevel));//最上级
            }else{
                rUser.setInviterId(0L);
                rUser.setTreeLevel(0L);
            }

            //注册信息
            String register = registerService.register(rUser);

            //TODO 设置默认角色
            SysUser suser = new SysUser();
            Long[] rols = {2L};
            suser.setRoleIds(rols);
            SysUser info2 = userMapper.checkUserNameUnique(openId);
            suser.setUserId(info2.getUserId());
            insertUserRole(suser);//设置角色

            // 生成令牌
            String token = loginService.login(openId,"systemUser"+lastSixCharacters, null,
                    null);

            map.put("code",200);
            map.put("token",token);

        }else{//登录
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysuser.getUserName(), "systemUser"+lastSixCharacters);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            loginService.recordLoginInfo(loginUser.getUserId());
            // 生成token
            String token = tokenService.createToken(loginUser);
            map.put("code",200);
            map.put(Constants.TOKEN, token);
        }

        return map;
    }


}
